package otel

import (
	"testing"
	"time"

	"github.com/qpoint-io/qtap/pkg/services/eventstore"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	noopotel "go.opentelemetry.io/otel/log/noop"
)

func TestEventStore_Save_Request(t *testing.T) {
	// Use noop logger to avoid actual OTLP export in tests
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	req := &eventstore.Request{
		Timestamp:   time.Now(),
		Direction:   "ingress",
		Url:         "https://api.example.com/users",
		URLPath:     "/users",
		Method:      "GET",
		Status:      200,
		Duration:    125,
		ContentType: "application/json",
		Category:    "api",
		Agent:       "curl/7.68.0",
		WrBytes:     0,
		RdBytes:     1024,
	}
	req.ConnectionID = "conn-123"
	req.EndpointId = "endpoint-456"
	req.RequestId = "req-789"
	req.Tags = []string{"tag1", "tag2"}
	req.AuthTokenMask = "****1234"
	req.AuthTokenHash = "abc123def456"
	req.AuthTokenSource = "header"
	req.AuthTokenType = "bearer"

	// Should not panic or error
	assert.NotPanics(t, func() {
		es.Save(t.Context(), req)
	})
}

func TestEventStore_Save_Issue(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	issue := &eventstore.Issue{
		Timestamp: time.Now(),
		Direction: "ingress",
		Error:     "Authentication failed",
		URL:       "https://api.example.com/secure",
		URLPath:   "/secure",
		Method:    "POST",
		Status:    401,
		TriggerConditions: []eventstore.IssueTriggerCondition{
			{Plugin: eventstore.PluginNameDetectErrors, Condition: "http_4xx"},
		},
		TriggerReasons: []string{"unauthorized_access", "invalid_token"},
	}
	issue.ConnectionID = "conn-123"
	issue.Tags = []string{"security", "error"}

	assert.NotPanics(t, func() {
		es.Save(t.Context(), issue)
	})
}

func TestEventStore_Save_PIIEntity(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	pii := &eventstore.PIIEntity{
		Timestamp:    time.Now(),
		EntityType:   "email",
		Score:        0.95,
		EntitySource: "request_body",
		FieldPath:    "user.email",
		ValueHash:    "hash123456",
	}
	pii.ConnectionID = "conn-123"
	pii.Tags = []string{"pii", "sensitive"}

	assert.NotPanics(t, func() {
		es.Save(t.Context(), pii)
	})
}

func TestEventStore_Save_ArtifactRecord(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	ar := &eventstore.ArtifactRecord{
		Timestamp: time.Now(),
		Type:      eventstore.ArtifactType_RequestBody,
		Digest:    "sha1:abcdef123456",
		URL:       "s3://bucket/path/to/artifact",
		Summary: map[string]any{
			"size":       1024,
			"compressed": true,
		},
	}
	ar.ConnectionID = "conn-123"

	assert.NotPanics(t, func() {
		es.Save(t.Context(), ar)
	})
}

func TestEventStore_Save_Connection(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	conn := &eventstore.Connection{
		Timestamp:      time.Now(),
		Finalized:      true,
		Direction:      eventstore.Direction_Ingress,
		VendorID:       "vendor-123",
		Part:           1,
		SocketProtocol: eventstore.SocketProtocol_TCP,
		L7Protocol:     eventstore.L7Protocol_HTTP1,
		BytesSent:      1024,
		BytesReceived:  2048,
		System: &eventstore.ConnectionSystem{
			Hostname:      "test-host",
			Agent:         "qtap",
			AgentInstance: "qtap-001",
		},
		Tags: map[string][]string{
			"env":    {"test"},
			"region": {"us-east-1"},
		},
	}
	conn.ConnectionID = "conn-123"

	assert.NotPanics(t, func() {
		es.Save(t.Context(), conn)
	})
}

func TestEventStore_Save_Artifact(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	artifact := &eventstore.Artifact{
		Type:        eventstore.ArtifactType_RequestBody,
		Data:        []byte(`{"user": "test", "email": "test@example.com"}`),
		ContentType: "application/json",
		Summary: map[string]any{
			"parsed": true,
		},
	}
	artifact.ConnectionID = "conn-123"

	// Should not panic - artifact saving is async via goroutine
	assert.NotPanics(t, func() {
		es.Save(t.Context(), artifact)
	})

	// Give goroutine time to complete
	time.Sleep(10 * time.Millisecond)
}

func TestEventStore_Save_UnsupportedType(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	// Test with unsupported type
	unsupported := struct {
		Field string
	}{Field: "test"}

	// Should not panic, just log warning
	assert.NotPanics(t, func() {
		es.Save(t.Context(), unsupported)
	})
}

func TestEventStore_SetConnection(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	// Test that connection can be set (just basic functionality)
	// We'll skip the complex connection creation for this test
	// and just test the basic Save functionality without connection

	// Test that events can be saved without connection
	req := &eventstore.Request{
		Timestamp: time.Now(),
		Method:    "GET",
		URLPath:   "/test",
		Status:    200,
	}

	// Save should work without connection
	assert.NotPanics(t, func() {
		es.Save(t.Context(), req)
	})
}

func TestEventStore_StructToAttributes(t *testing.T) {
	es := &EventStore{
		logger:      noopotel.NewLoggerProvider().Logger("test"),
		serviceName: "test-qtap",
		environment: "test",
	}

	// Test with a simple request struct
	req := &eventstore.Request{
		Timestamp: time.Now(),
		Method:    "GET",
		URLPath:   "/test",
		Status:    200,
	}
	req.ConnectionID = "conn-123"

	attrs, err := es.structToAttributes(req)
	require.NoError(t, err)
	require.NotEmpty(t, attrs)

	// Should contain basic fields
	found := false
	for _, attr := range attrs {
		if attr.Key == "method" {
			found = true
			break
		}
	}
	require.True(t, found, "Should contain method attribute")
}
